﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using Daphne.Presenters;
using System.Reflection;
using GameEngine;
using System.Windows.Forms;
using DaphneProfiBase;

namespace EndGameDBTester
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private ProfessionalGameBoardPresenter _presenter;
        private GameManager _mng;
        private ComputerEngine.Engine _engine = null;
        private EGTBSelection _selectionConfig = null;

        private string _keyEGTBPathConfig = "EGTBPathConfig";

        public MainWindow()
        {
            InitializeComponent();

            // create default game manager
            _mng = GameManagerFactory.CreateGameManager(enumGameType.Czech);
            _mng.GameBoard.SetupStartPosition(3);

            // get app path
            string sAppPath = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            // initialize checkersboard
            _presenter = new ProfessionalGameBoardPresenter();
            _presenter.GameManager = _mng;
            _presenter.MoveDoneEvent += new TMoveDoneDelegate(_presenter_MoveDoneEvent);
            uxBoard2D.SetPresenter(_presenter);
            uxBoard2D.LoadImages(sAppPath);

            // set handlers
            uxBoardCanvas.LayoutUpdated += uxBoardCanvas_LayoutUpdated;
            uxEditor.Click += uxEditor_Click;
            uxClearBoard.Click += uxClearBoard_Click;
            uxWhiteToMove.Click += uxWhiteToMove_Click;
            uxBlackToMove.Click += uxBlackToMove_Click;
            uxStart.Click += uxStart_Click;
            uxBack.Click += uxBack_Click;
            uxForward.Click += uxForward_Click;
            uxFinish.Click += uxFinish_Click;
            uxMoveList.SelectionChanged += uxMoveList_SelectionChanged;
            uxMoveList.MouseDoubleClick += uxMoveList_MouseDoubleClick;
            this.Closing += MainWindow_Closing;
            btnFindFolder.Click += btnFindFolder_Click;

            // get EGTB config
            _selectionConfig = Config.Get<EGTBSelection>(_keyEGTBPathConfig);
            if (_selectionConfig != null)
            {
                InitEngine(_selectionConfig);
            }
            else 
                _selectionConfig = new EGTBSelection();

            // form initalization
            UpdateUI();           
        }

        #region Event Handlers     

        /// <summary>
        /// User Move Done Completed
        /// </summary>
        void _presenter_MoveDoneEvent(Move move)
        {
            _mng.FinalizeMove(move);
            UpdateUI();
        }

        /// <summary>
        /// Computer Engine Resources Clean Up.
        /// </summary>
        void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            CleanUpEngine();
        }

        /// <summary>
        /// Looks up the folder where the EGTB is placed
        /// </summary>
        void btnFindFolder_Click(object sender, RoutedEventArgs e)
        {
            FolderBrowserDialog dlg = new FolderBrowserDialog();
            dlg.ShowNewFolderButton = true;
            if (string.IsNullOrWhiteSpace(uxEGTBPath.Text) == false)
                dlg.SelectedPath = uxEGTBPath.Text;
            if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                uxEGTBPath.Text = dlg.SelectedPath;

                RetrieveInput();
                InitEngine(_selectionConfig);
                Config.Set<EGTBSelection>(_keyEGTBPathConfig, _selectionConfig);
                UpdateUI();
            }
        }

        /// <summary>
        /// Starts / Exits edit mode
        /// </summary>
        void uxEditor_Click(object sender, RoutedEventArgs e)
        {
            _presenter.ToggleEditMode();
            _mng.MoveHistory.ClearMoves();
            uxBoard2D.Refresh();
            UpdateUI();
        }

        /// <summary>
        /// Clears all figures on the board - run only from the editor setup mode
        /// </summary>
        void uxClearBoard_Click(object sender, RoutedEventArgs e)
        {
            _mng.GameBoard.CleanBoard();
            uxBoard2D.Refresh();
        }

        /// <summary>
        /// Set STM White
        /// </summary>
        void uxWhiteToMove_Click(object sender, RoutedEventArgs e)
        {
            _mng.GameBoard.SetOnMove(enumFigureColor.White);
            _mng.MoveHistory.ClearMoves();
            if (_presenter.IsInEditMode())
                return;
            UpdateUI();
        }

        /// <summary>
        /// Set STM Black
        /// </summary>
        void uxBlackToMove_Click(object sender, RoutedEventArgs e)
        {
            _mng.GameBoard.SetOnMove(enumFigureColor.Black);
            _mng.MoveHistory.ClearMoves();
            if (_presenter.IsInEditMode())
                return;
            UpdateUI();
        }

        /// <summary>
        /// Handler for moving game to the beginning
        /// </summary>
        void uxStart_Click(object sender, RoutedEventArgs e)
        {
            if (_mng.MoveHistory.CanMoveBack())
                _mng.MoveHistory.MoveToBeginning();
            uxBoard2D.Refresh();
            UpdateUI();
        }

        /// <summary>
        /// Handler for making one move back
        /// </summary>
        void uxBack_Click(object sender, RoutedEventArgs e)
        {
            if (_mng.MoveHistory.CanMoveBack())
                _mng.MoveHistory.MoveBack();
            uxBoard2D.Refresh();
            UpdateUI();
        }

        /// <summary>
        /// Handler for making one move forward
        /// </summary>
        void uxForward_Click(object sender, RoutedEventArgs e)
        {
            if (_mng.MoveHistory.CanMoveForward())
                _mng.MoveHistory.MoveForward();
            uxBoard2D.Refresh();
            UpdateUI();
        }

        /// <summary>
        /// Handler for moving the game to the very end
        /// </summary>
        void uxFinish_Click(object sender, RoutedEventArgs e)
        {
            if (_mng.MoveHistory.CanMoveForward())
                _mng.MoveHistory.MoveToEnd();
            uxBoard2D.Refresh();
            UpdateUI();
        }

        /// <summary>
        /// Handle the change in selection for showing the move
        /// </summary>
        void uxMoveList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            int index = uxMoveList.SelectedIndex;
            if (index < 0)
            {
                _presenter.MoveToShow = null;
            }
            else
            {
                _presenter.MoveToShow = (uxMoveList.SelectedValue as MoveRecord).Move;
            }

            // update checkers board
            uxBoard2D.Refresh();
        }

        /// <summary>
        /// Check for item double click
        /// </summary>
        void uxMoveList_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
            {
                int index = uxMoveList.SelectedIndex;
                if (index >= 0)
                {
                    // get and do the move
                    Move move = (uxMoveList.SelectedValue as MoveRecord).Move;
                    _mng.DoMove(move);

                    // UI update
                    UpdateUI();
                }
            }
        }

        /// <summary>
        /// Checkers Board Layout Calculation
        /// </summary>
        void uxBoardCanvas_LayoutUpdated(object sender, EventArgs e)
        {
            // calculate board size
            double width = uxBoardCanvas.ActualWidth;
            double height = uxBoardCanvas.ActualHeight;
            double boardSize = Math.Min(width, height);

            // calculate board position
            double boardX = (width - boardSize) / 2;
            //double boardY = (height - boardSize) / 2;
            double boardY = 0;

            // set board parameters
            Canvas.SetLeft(uxBoard2D, boardX);
            Canvas.SetTop(uxBoard2D, boardY);
            uxBoard2D.Width = boardSize;
            uxBoard2D.Height = boardSize;           
        }

        #endregion

        #region UI Update Methods

        /// <summary>
        /// Retrieves all the input to the output container class
        /// </summary>
        private void RetrieveInput()
        {
            /// create container class
            _selectionConfig = new EGTBSelection();

            // get EGTB Path
            _selectionConfig.EGTBPath = uxEGTBPath.Text.Trim();
        }

        /// <summary>
        /// Updates which controls and enabled and which not
        /// </summary>
        private void UpdateUI()
        {
            // resolve edit mode
            bool editMode = _presenter.IsInEditMode();

            // set enabled/disabled
            uxClearBoard.IsEnabled = editMode == true;
            uxStart.IsEnabled = editMode == false;
            uxBack.IsEnabled = editMode == false;
            uxForward.IsEnabled = editMode == false;
            uxFinish.IsEnabled = editMode == false;

            // set STM
            uxWhiteToMove.IsChecked = _mng.GameBoard.OnMove == enumFigureColor.White;
            uxBlackToMove.IsChecked = _mng.GameBoard.OnMove == enumFigureColor.Black;

            // update EGTB info
            string engineInfo = string.Empty;
            if (_engine != null)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("DraughtsType: ");

                // resolve draughts type
                ComputerEngine.eCheckersType checkersType = _engine.GetEGTBDraughtType();
                enumGameType gameType = checkersType.ToGameEngineType();
                DraughtsType type = DraughtsTypeList.FirstOrDefault(i => i.GameType == gameType);
                if (type != null)
                    sb.Append(type.Name);
                
                // add maximum number of pieces of EGTB
                sb.Append("   Max pieces: ");
                sb.Append(_engine.GetEGTBMaxPiece().ToString());

                engineInfo = sb.ToString();             
            }
            uxEGTBInfo.Text = engineInfo;
            uxEGTBPath.Text = _selectionConfig.EGTBPath;

            // do position probing
            ProbePosition();
        }

        /// <summary>
        /// Updates the list of position
        /// </summary>
        private void ProbePosition()
        {
            if (_engine == null)
            {
                uxCurrentPosition.Text = string.Empty;
            }
            else
            {
                // convert board
                ComputerEngine.CheckersBoard cb = ConvertBoard(_mng.GameBoard);
                bool bWhite = _mng.GameBoard.OnMove == enumFigureColor.White;

                // probe the current position
                ComputerEngine.eGameResult result = ComputerEngine.eGameResult.Unknown;
                int dtm = 0;
                result = _engine.DBLookUp(cb, bWhite, ref dtm);

                // perform UI Update
                UpdateCurrentPosition(result, dtm);
            }

            // probe all possible moves
            ProbePossibleMoves();
        }

        /// <summary>
        /// Updates the current position
        /// </summary>
        private void UpdateCurrentPosition(ComputerEngine.eGameResult result, int dtm)
        {
            string s = GameResultToString(result);
            if (result == ComputerEngine.eGameResult.WhiteWin ||
                result == ComputerEngine.eGameResult.BlackWin)
            {
                s += "  DTM: " + dtm.ToString();            
            }

            uxCurrentPosition.Text = s;
        }

        /// <summary>
        /// Generates and probes all possible moves
        /// </summary>
        private void ProbePossibleMoves()
        {
            // generate move list
            List<Move> possibleMoves = _mng.GenerateAllMoves();

            // bind the UI
            List<MoveRecord> moveRecList = new List<MoveRecord>();
            foreach (var move in possibleMoves)
            {
                MoveRecord moveRec= new MoveRecord();
                moveRec.Move = move;
                moveRec.MoveTitle = _mng.MoveToString(move, false);
                moveRec.GameResult = null;
                moveRec.DTM = null;

                // probe the position
                if (_engine != null)
                {
                    _mng.DoMoveStep(move);
                    _mng.GameBoard.ChangeOnMove();

                    // probe the next position
                    int dtm = 0;
                    ComputerEngine.CheckersBoard cb = ConvertBoard(_mng.GameBoard);
                    bool bWhite = _mng.GameBoard.OnMove == enumFigureColor.White;
                    ComputerEngine.eGameResult result = _engine.DBLookUp(cb, bWhite, ref dtm);

                    // convert output
                    moveRec.GameResult = GameResultToString(result);
                    if (result == ComputerEngine.eGameResult.WhiteWin
                        || result == ComputerEngine.eGameResult.BlackWin)
                    {
                        moveRec.DTM = dtm;
                    }

                    _mng.GameBoard.ChangeOnMove();
                    _mng.UndoMoveStep(move);
                }

                moveRecList.Add(moveRec);
            }

            uxMoveList.ItemsSource = null;
            uxMoveList.ItemsSource = moveRecList;
        }

        #endregion

        #region Engine EGTB Methods

        /// <summary>
        /// Initializes engine
        /// </summary>
        private void InitEngine(EGTBSelection settings)
        {
            if (_engine != null)
            {
                CleanUpEngine();
            }

            try
            {
                _engine = new ComputerEngine.Engine();
                _engine.DBLookUpInit(settings.EGTBPath);
            }
            catch (Exception ex)
            {
                _engine = null;
                System.Windows.MessageBox.Show("Error initializing engine: " + ex.Message);
            }             
        }

        /// <summary>
        /// Frees engine resources
        /// </summary>
        private void CleanUpEngine()
        {
            if (_engine != null)
            {
                _engine.DBLookUpClose();
                _engine.TerminateComputer();
                _engine = null;
            }
        }


        #endregion

        #region Various Helper Methods

        /// <summary>
        /// Converts the result to the text
        /// </summary>
        private string GameResultToString(ComputerEngine.eGameResult result)
        {
            switch (result)
            {
                case ComputerEngine.eGameResult.WhiteWin:
                    return "WHITE WIN";
                case ComputerEngine.eGameResult.BlackWin:
                    return "BLACK WIN";
                case ComputerEngine.eGameResult.Draw:
                    return "DRAW";
                case ComputerEngine.eGameResult.Unknown:
                    return "UNKNOWN";
                default:
                    return "ERROR";
            }
        }

        /// <summary>
        /// CheckersBoard Model conversion for the engine
        /// </summary>
        /// <param name="board"></param>
        /// <returns></returns>
        ComputerEngine.CheckersBoard ConvertBoard(GameBoard board)
        {
            ComputerEngine.CheckersBoard compBoard = new ComputerEngine.CheckersBoard();
            compBoard.Board = new ComputerEngine.Coordinate[8, 8];
            compBoard.BoardSize = 8;

            for (int row = 0; row < 8; row++)
            {
                for (int col = 0; col < 8; col++)
                {
                    switch (board.GetField(row, col))
                    {
                        case enumFigureType.Nothing:
                            compBoard.Board[row, col]
                                = new ComputerEngine.Coordinate(row, col, ComputerEngine.eFigureType.Nothing);
                            break;
                        case enumFigureType.WhiteMan:
                            compBoard.Board[row, col]
                                = new ComputerEngine.Coordinate(row, col, ComputerEngine.eFigureType.WhiteMan);
                            break;
                        case enumFigureType.BlackMan:
                            compBoard.Board[row, col]
                                = new ComputerEngine.Coordinate(row, col, ComputerEngine.eFigureType.BlackMan);
                            break;
                        case enumFigureType.WhiteKing:
                            compBoard.Board[row, col]
                                = new ComputerEngine.Coordinate(row, col, ComputerEngine.eFigureType.WhiteKing);
                            break;
                        case enumFigureType.BlackKing:
                            compBoard.Board[row, col]
                                = new ComputerEngine.Coordinate(row, col, ComputerEngine.eFigureType.BlackKing);
                            break;
                    }


                }
            }

            return compBoard;
        }     
        
        #endregion

        #region Helper Private Classes

        /// <summary>
        /// Binding move record class
        /// </summary>
        private class MoveRecord
        {
            public Move Move
            { get; set; }

            public string MoveTitle
            { get; set; }

            public string GameResult
            { get; set; }

            public int? DTM
            { get; set; }
        }

        /// <summary>
        /// The list of supported checkers
        /// </summary>
        private List<DraughtsType> DraughtsTypeList = new List<DraughtsType>()
        {
            new DraughtsType { GameType = enumGameType.Czech, Name = "Czech draughts"},
            new DraughtsType { GameType = enumGameType.English, Name = "English checkers"},
            new DraughtsType { GameType = enumGameType.International, Name = "International draughts"},
            new DraughtsType { GameType = enumGameType.Italian, Name = "Italian draughts"},
            new DraughtsType { GameType = enumGameType.Russian, Name = "Russian draughts"},
            new DraughtsType { GameType = enumGameType.Spanish, Name = "Spanish draughts"}
        };

        /// <summary>
        /// Binding class for checkers type
        /// </summary>
        private class DraughtsType
        {
            public enumGameType GameType
            { get; set; }

            public string Name
            { get; set; }

            public override string ToString()
            {
                return Name;
            }
        }

        #endregion

    }
}
